

cmake_minimum_required(VERSION 3.16)

project(solar_2 VERSION 0.1 LANGUAGES C CXX CUDA)

list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")

include(CTest)
enable_testing()

find_package(CUDAToolkit REQUIRED)

if("${CUDAToolkit_VERSION}" VERSION_LESS "12.0")
    message(FATAL_ERROR "CUDA Toolkit should be at least 12")
endif()

set(CMAKE_AUTORCC ON) # 这一句如果不加的话，使用不了qrc下的相对路径
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)

# 改为CUDA C++ 20？（Baseline：CUDA 12）
set(CMAKE_CUDA_STANDARD 20)
set(CMAKE_CUDA_STANDARD_REQUIRED TRUE)
set(CUDA_SEPARABLE_COMPILATION ON)

list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_23 CXX23_SUPPORTED)

if(CXX23_SUPPORTED)
    set(CMAKE_CXX_STANDARD 23)
endif()

if(CMAKE_CUDA_COMPILER_ID MATCHES "Clang" AND CMAKE_CUDA_COMPILER_VERSION VERSION_LESS "19.0.0")
    # LLVM Github Issue #88883
    if(MSVC AND(CMAKE_LINKER MATCHES "lld-link.exe" OR CMAKE_LINKER MATCHES "link.exe"))
        set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /OPT:NOICF")
        set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /OPT:NOICF")
    endif()
endif()

# nvcc可能不支持c++20/23，但是Clang支持
# list(FIND CMAKE_CUDA_COMPILE_FEATURES cuda_std_20 CUDA20_SUPPORTED)
# if(CUDA20_SUPPORTED)
# set(CMAKE_CUDA_STANDARD 20)
# endif()
set(CUDA20_SUPPORTED TRUE)

list(FIND CMAKE_CUDA_COMPILE_FEATURES cuda_std_23 CUDA23_SUPPORTED)

if(CUDA23_SUPPORTED)
    set(CMAKE_CUDA_STANDARD 23)
endif()

# include(CheckLanguage)
# check_language(CUDA)
# set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--stack,999999")
string(TOUPPER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_UPPER)
set(CMAKE_CXX_FLAGS_BUILD_TYPE "CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UPPER}")
set(TRY_COMPILE_STD_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${${CMAKE_CXX_FLAGS_BUILD_TYPE}}")

try_compile(
    HAVE_MSVC_STL
    "${CMAKE_SOURCE_DIR}/cmake"
    "${CMAKE_SOURCE_DIR}/cmake/try_compile_msvc_stl.cpp"
    COMPILE_DEFINITIONS "${TRY_COMPILE_STD_CXX_FLAGS}"
    OUTPUT_VARIABLE TRY_COMPILE_OUTPUT
)

try_compile(
    HAVE_LIBCXX
    "${CMAKE_SOURCE_DIR}/cmake"
    "${CMAKE_SOURCE_DIR}/cmake/try_compile_libcxx.cpp"
    COMPILE_DEFINITIONS "${TRY_COMPILE_STD_CXX_FLAGS}"
    OUTPUT_VARIABLE TRY_COMPILE_OUTPUT
)

try_compile(
    HAVE_LIBSTDCXX
    "${CMAKE_SOURCE_DIR}/cmake"
    "${CMAKE_SOURCE_DIR}/cmake/try_compile_libstdc++.cpp"
    COMPILE_DEFINITIONS "${TRY_COMPILE_STD_CXX_FLAGS}"
    OUTPUT_VARIABLE TRY_COMPILE_OUTPUT
)

set(STD_FORMAT_EXTRA_FLAGS "")

if(HAVE_MSVC_STL)
    message(STATUS "Using MSVC STL")
endif()

if(HAVE_LIBCXX)
    message(STATUS "Using LLVM libc++")
    set(STD_FORMAT_EXTRA_FLAGS "-D_LIBCPP_ENABLE_EXPERIMENTAL")
endif()

if(HAVE_LIBSTDCXX)
    message(STATUS "Using GNU libstdc++")
endif()

if(NOT(HAVE_MSVC_STL OR HAVE_LIBCXX OR HAVE_LIBSTDCXX))
    message(WARNING "Unknown Standard C++ Library")
endif()

# libc++ (Clang's C++ Library 需要-D_LIBCPP_ENABLE_EXPERIMENTAL来启用std::format)
# MSVC 无需额外设置
# GCC/G++的libstdc++没测试过，不知道是否需要额外的宏定义，可补充
set(TRY_COMPILE_STD_FORMAT_CXX_FLAGS "${TRY_COMPILE_STD_CXX_FLAGS} ${STD_FORMAT_EXTRA_FLAGS}")

try_compile(
    HAVE_STD_FORMAT
    "${CMAKE_SOURCE_DIR}/cmake"
    "${CMAKE_SOURCE_DIR}/cmake/try_compile_std_format.cpp"
    COMPILE_DEFINITIONS "${TRY_COMPILE_STD_FORMAT_CXX_FLAGS}"
    OUTPUT_VARIABLE TRY_COMPILE_OUTPUT
)

message(STATUS "HAVE_STD_FORMAT: ${HAVE_STD_FORMAT}")

try_compile(
    HAVE_STD_PRINT
    "${CMAKE_SOURCE_DIR}/cmake"
    "${CMAKE_SOURCE_DIR}/cmake/try_compile_std_print.cpp"
    COMPILE_DEFINITIONS "${TRY_COMPILE_STD_FORMAT_CXX_FLAGS}"
    OUTPUT_VARIABLE TRY_COMPILE_OUTPUT
)

message(STATUS "HAVE_STD_PRINT: ${HAVE_STD_PRINT}")

include(CheckCompilerFlag)

if(MSVC)
    add_compile_definitions(_USE_MATH_DEFINES)
endif()

check_compiler_flag(CXX -Wmissing-template-arg-list-after-template-kw CXX_COMPILER_SUPPORTS_WMISSING_TEMPLATE_ARG_LIST_AFTER_TEMPLATE_KW)
check_compiler_flag(CUDA -Wmissing-template-arg-list-after-template-kw CUDA_COMPILER_SUPPORTS_WMISSING_TEMPLATE_ARG_LIST_AFTER_TEMPLATE_KW)

if(CXX_COMPILER_SUPPORTS_WMISSING_TEMPLATE_ARG_LIST_AFTER_TEMPLATE_KW)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-template-arg-list-after-template-kw")
endif()

if(CUDA_COMPILER_SUPPORTS_WMISSING_TEMPLATE_ARG_LIST_AFTER_TEMPLATE_KW OR((${CMAKE_CXX_COMPILER} STREQUAL ${CMAKE_CUDA_COMPILER}) AND CXX_COMPILER_SUPPORTS_WMISSING_TEMPLATE_ARG_LIST_AFTER_TEMPLATE_KW))
    set(CUDA_COMPILER_SUPPORTS_WMISSING_TEMPLATE_ARG_LIST_AFTER_TEMPLATE_KW TRUE)
    set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-missing-template-arg-list-after-template-kw")
endif()

add_subdirectory("third_party/vectorclass2")

# list(APPEND CMAKE_PREFIX_PATH /home/liu/Qt/6.4.3/gcc_64/lib/cmake)

# 手动把qt6.4.3的目录添加进去
# message("cmake_module_path: " ${CMAKE_MODULE_PATH})
find_package(Qt6 6.4 REQUIRED COMPONENTS Quick)
find_package(Qt6 REQUIRED COMPONENTS DataVisualization) # 数据可视化的部分
find_package(Qt6 REQUIRED COMPONENTS Gui)

# set(BOOST_ROOT /home/liu/Git_resposity/common_third_party/boost_1_82_0) # 设置boost库搜索路径
# set(Boost_NO_SYSTEM_PATHS ON) # 只搜索上语句设置的搜索路径
find_package(Boost REQUIRED)

# message(STATUS "boost include path is: ${Boost_INCLUDE_DIRS}")
# message(STATUS "boost include path is : ${Boost_INCLUDE_DIRS}")
# message(STATUS "boost library path is : ${Boost_LIBRARY_DIRS}")
# message(STATUS "boost libraries is : ${Boost_LIBRARIES}")
string(REGEX REPLACE "/include.*$" "" Boost_ROOT ${Boost_INCLUDE_DIRS})
set(Boost_LIBRARY_DIR "${Boost_ROOT}/lib" CACHE PATH "${Boost_ROOT}/lib")
include_directories(${Boost_INCLUDE_DIRS})
link_directories(${Boost_LIBRARY_DIRS})

# cuda 库

# set(CUDA_TOOLKIT_ROOT_DIR "/usr/local/cuda-12.0")

# message("是否找到了cuda: " ${CUDA_FOUND})
message("CUDA_TOOLKIT_ROOT_DIR: ${CUDA_TOOLKIT_ROOT_DIR}")
message("cuda version: ${CUDA_VERSION_STRING}")

# list(APPEND CUDA_LIBRARIES "${CUDA_TOOKIT_ROOT_DIR}/lib64/libcudadevrt.a")
message(STATUS "CUDA libraries: ${CUDA_LIBRARIES}")
message(STATUS "CUDA include path: ${CUDA_INCLUDE_DIRS}")
message(STATUS "CUDA_LIBRARY_DIRS: ${CUDA_LIBRARY_DIRS}")
include_directories(${CUDA_INCLUDE_DIRS})

if(CMAKE_SYSTEM_NAME MATCHES "Windows")
    link_directories(${CUDA_TOOLKIT_ROOT_DIR}/lib/x64)
else()
    link_directories(${CUDA_TOOLKIT_ROOT_DIR}/lib64)
endif()

if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES)
    set(CMAKE_CUDA_ARCHITECTURES 61)
endif()

message(STATUS "CMAKE_CUDA_FLAGS: ${CUDA_NVCC_FLAGS}")

# list(APPEND CMAKE_CUDA_FLAGS "-std=c++11;-rdc=true;-dc")
set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} -gencode;-lcudadevrt;-extended-lambda)
message(STATUS "CUDA_NVCC_FLAGS: ${CUDA_NVCC_FLAGS}")

set(CUDA_SEPARABLE_COMPILATION ON)

if("NVIDIA" STREQUAL "${CMAKE_CUDA_COMPILER_ID}")
    set(CUDA_PROPAGATE_HOST_FLAGS OFF)
endif()

# set(ANOTHER_CUDA_INCLUDE_DIRS "/usr/local/cuda-12.0/targets/x86_64-linux/include")
# include_directories( ${ANOTHER_CUDA_INCLUDE_DIRS})

# # fmt
# set(fmt_DIR /home/liu/Git_resposity/common_third_party/fmt/usr/local/lib/cmake/fmt )    # 手动设置fmt库的搜索路径
# find_package(fmt REQUIRED)
# link_libraries(fmt::fmt)

# 自动查找头文件路径函数
# macro(FIND_INCLUDE_DIR result curdir)  #定义函数,2个参数:存放结果result；指定路径curdir；
# file(GLOB_RECURSE children "${curdir}/*.hpp" "${curdir}/*.h" "${curdir}/*.cuh" ) # 遍历获取{curdir}中*.hpp和*.h文件列表
# # message(STATUS "children= ${children}")								# 打印*.hpp和*.h的文件列表
# set(dirlist "")														# 定义dirlist中间变量，并初始化
# foreach(child ${children})											# for循环
# string(REGEX REPLACE "(.*)/.*" "\\1" LIB_NAME ${child})			# 字符串替换,用/前的字符替换/*h
# if(IS_DIRECTORY ${LIB_NAME})									# 判断是否为路径
# LIST(APPEND dirlist ${LIB_NAME})							# 将合法的路径加入dirlist变量中
# endif()															# 结束判断
# endforeach()														# 结束for循环
# list(REMOVE_DUPLICATES dirlist)                                     # 删除重复的文件夹
# set(${result} ${dirlist})											# dirlist结果放入result变量中
# endmacro()																# 函数结束

# 查找include目录下的所有*.hpp,*.h,*.cuh头文件,并路径列表保存到 INCLUDE_DIR_LIST 变量中
# set(ROOT_DIR "${CMAKE_SOURCE_DIR}")
# FIND_INCLUDE_DIR(UI_INCLUDE_DIR_LIST ${ROOT_DIR}/source/ui)			# 调用函数，指定参数
# FIND_INCLUDE_DIR(ALGORITHM_INCLUDE_DIR_LIST ${ROOT_DIR}/source/algorithm)
# FIND_INCLUDE_DIR(TEST_INCLUDE_DIR_LIST ${ROOT_DIR}/source/test_folder)

# 将INCLUDE_DIR_LIST中路径列表加入工程
# include_directories(   												# INCLUDE_DIR_LIST路径列表加入工程
# ${UI_INCLUDE_DIR_LIST}
# ${TEST_INCLUDE_DIR_LIST}
# ${ALGORITHM_INCLUDE_DIR_LIST}
# ${ROOT_DIR}/source/ui
# ${ROOT_DIR}/source/algorithm
# ${ROOT_DIR}/third_party/vectorclass2
# )

# ============================
# 测试--是否包含bits/c++config.h文件
# set(TEST_DIR_1 "/usr/include/x86_64-linux-gnu/c++/11")
# set(TEST_DIR_2 "/usr/include/")
# include_directories(    ${TEST_DIR_1}  )

# 测试结果：通过这种方法显式设置bits/c++config.h的包含路径，最终代码可以运行，但是在IDE中会标红
# !!!!!!!!不知道是不是编译器的原因，这里待修改！！！！！！！！！！
# ===========================

# find_package(Python3 COMPONENTS Development NumPy)
# include_directories("/home/liu/myfiles/code/reposities/matplotlib-cpp/")

# add_subdirectory(source)
qt_standard_project_setup()

# file(GLOB_RECURSE native_srcs source/*.cpp source/*.h)
# file(GLOB_RECURSE native_srcs
# source/*.cpp source/*.h
# source/*.cu source/*.cuh
# )
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
set(SOLAR_WITH_QT TRUE)
set(STRF_CUDA_SUPPORT ON)

# add_subdirectory(third_party/strf)
# add_subdirectory(third_party/cuda-kat)
add_subdirectory(third_party/glad)
add_subdirectory(third_party/glm)
add_subdirectory(third_party/gcem)
add_subdirectory(source/algorithm)
add_subdirectory(source/ui)
add_subdirectory(source/utils)

add_executable(appsolar_2
    qml.qrc
    src/main.cpp
    solar_app.manifest

    # ${native_srcs}
)

# file(GLOB_RECURSE qml_files ${PROJECT_SOURCE_DIR}/*.qml)
qt_add_qml_module(appsolar_2
    URI solar_2
    VERSION 1.0
    QML_FILES zqml/Main.qml
    QML_FILES zqml/MyRect_World.qml
    QML_FILES zqml/MyRect_Topology.qml
    QML_FILES zqml/TopoTreeView.qml
    QML_FILES zqml/ParaTreeView.qml
    QML_FILES zqml/RayTracingWindow.qml
    QML_FILES zqml/AnalyticFunctionWindow.qml
    QML_FILES zqml/DataVisualizationWindow.qml
    QML_FILES zqml/ParaRect.qml
    QML_FILES zqml/FluxMap.qml

    # QML_FILES ${qml_files}
)

set_target_properties(appsolar_2 PROPERTIES
    MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
    MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
    MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
    MACOSX_BUNDLE TRUE
    WIN32_EXECUTABLE TRUE
)

# ============================
# find_package(LIBPNG REQUIRED)
# include_directories(${LIBPNG_INCLUDE_DIRS})
# link_directories(${LIBPNG_LIBRARY_DIRS})
# link_libraries(${LIBPNG_LIBRARIES})
find_package(PNG REQUIRED)

# include_directories(${PNG_INCLUDE_DIR})
target_link_libraries(appsolar_2 PUBLIC PNG::PNG)

# ===========================

# file(GLOB_RECURSE native_includes source/topology/*.h)
# add_library(DIR_INCLUDE SHARED ${native_includes})
# target_link_libraries(
# appsolar_2 PRIVATE DIR_INCLUDE
# )

# file(GLOB_RECURSE native_srcs source/*.cpp source/*.h)
# add_library(DIR_SRC_ SHARED ${native_srcs})
# target_link_libraries(
# appsolar_2 PRIVATE DIR_SRC
# )
target_link_libraries(appsolar_2 PRIVATE VectorClass2)
target_include_directories(appsolar_2 PRIVATE "include")
target_include_directories(appsolar_2 PRIVATE "source")
target_include_directories(appsolar_2 PRIVATE ${Boost_INCLUDE_DIRS})
target_link_directories(appsolar_2 PRIVATE ${Boost_LIBRARY_DIR})

target_link_libraries(appsolar_2 PUBLIC CUDA::curand) # 解决bug：undefined reference to 'curandCreateGenerator'

if(HAVE_STD_FORMAT)
    target_compile_definitions(appsolar_2 PRIVATE "HAVE_STD_FORMAT")
    target_compile_options(appsolar_2 PRIVATE "${STD_FORMAT_EXTRA_FLAGS}")
else()
    # set(fmt_DIR /home/liu/Git_resposity/common_third_party/fmt/usr/local/lib/cmake/fmt )    # 手动设置fmt库的搜索路径
    find_package(fmt REQUIRED)
    target_link_libraries(appsolar_2 PRIVATE fmt::fmt)
endif()

if(HAVE_STD_PRINT)
    target_compile_definitions(appsolar_2 PRIVATE "HAVE_STD_PRINT")
    target_compile_options(appsolar_2 PRIVATE "${STD_FORMAT_EXTRA_FLAGS}")

    # target_compile_options(appsolar_2 PRIVATE "-clang:-std=c++23")
endif()

target_link_libraries(appsolar_2
    PRIVATE Qt6::Quick
    PRIVATE Qt6::DataVisualization
    PRIVATE Qt6::Gui
)

# target_include_directories(appsolar_2 PRIVATE
# ${Python3_INCLUDE_DIRS} ${Python3_NumPy_INCLUDE_DIRS})
# target_link_libraries(appsolar_2 PUBLIC Python3::Python Python3::NumPy)

# set(libpng_DIR /usr/lib/x86_64-linux-gnu)
# target_link_libraries(appsolar_2 PUBLIC ${libpng_DIR}/libpng)
set_property(TARGET appsolar_2 PROPERTY CUDA_SEPARABLE_COMPILATION OFF)

target_link_libraries(appsolar_2 PRIVATE ${CUDA_LIBRARIES})
target_link_libraries(appsolar_2 PRIVATE solar_ui)
target_link_libraries(appsolar_2 PRIVATE solar_ui_cuda)
target_link_libraries(appsolar_2 PRIVATE solar_utils)
target_link_libraries(appsolar_2 PRIVATE solar_utils_cuda)
target_link_libraries(appsolar_2 PRIVATE solar_algorithms)
target_link_libraries(appsolar_2 PRIVATE solar_algorithms_cuda)
target_link_libraries(appsolar_2 PRIVATE solar_algorithms_cuda_with_thrust)
target_include_directories(appsolar_2 PUBLIC "${CMAKE_SOURCE_DIR}/source")

if(CMAKE_SYSTEM_NAME MATCHES "Windows")
    target_link_libraries(appsolar_2 PRIVATE Opengl32.lib)
endif()

# target_compile_options(appsolar_2 PRIVATE $<$<COMPILE_LANGUAGE:CUDA>: -rdc=true> )
install(TARGETS appsolar_2
    BUNDLE DESTINATION .
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
